#include <iostream>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

//全局的命令行参数表
char *gargv[argvnum];//命令行参数表
int gargc = 0;//计数

//上一个进程的退出码
int lastcode = 0;

//全局的shell工作路径
char pwd[basesize];
char pwdenv[basesize];

//myshell的环境变量表
char *genv[envnum];

string GetUserName()
{
	string name = getenv("USER");
	return name.empty() ? "None" : name;
}
string GetHostName()
{
	string hostname = getenv("HOSTNAME");
	return hostname.empty() ? "None" : hostname;
}
string GetPwd()
{
	//从系统中获取
	if (getcwd(pwd, sizeof(pwd)) == NULL)
	{
		return "None";
	}
	snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
	putenv(pwdenv);//PWD=xxx  更新环境变量
	return pwd;
//	string pwd = getenv("PWD");
//	return pwd.empty() ? "None" : pwd;
}

string LastDir()
{
	//  /home/yjz/code/xxx
	string cur = GetPwd();
	if (cur == "/" || cur == "None")
		return cur;
	size_t pos1 = cur.rfind("/");
	if (pos1 == string::npos)
		return cur;
	size_t pos2 = pos1 == 0 ? pos1 : pos1 - 1;
	size_t pos3 = cur.rfind("/", pos2);
	if (pos3 == string::npos)
		return cur;
	return cur.substr(pos3);
}

string MakeCmdLine()
{
	char cmd_line[basesize];
	snprintf(cmd_line, basesize, "%s@%s:~%s$ ", GetUserName().c_str(), GetHostName().c_str(), LastDir().c_str());
	return cmd_line;
}

void PrintCmdLine()
{
	printf("%s", MakeCmdLine().c_str());
	fflush(stdout);
}

bool GetCmdLine(char buffer[], int size)
{
	char *result = fgets(buffer, size, stdin);//从标准输入流中读取输入指令
	if (result)
	{
    	buffer[strlen(buffer) - 1] = '\0';//去掉输入指令时的回车键
		if (strlen(buffer) == 0)
		{
			return false;//如果是空串直接返回
		}
		return true;
	}
	return false;
}

void ParseCmdLine(char buffer[], int len)
{
	//初始化
	memset(gargv, 0, sizeof(gargv));
	gargc = 0;

	const char *delim = " ";
	gargv[gargc++] = strtok(buffer, delim);
	while (gargv[gargc++] = strtok(NULL, delim));
	gargc--;
}

//void debug()
//{
//	printf("argc: %d\n", gargc);
//	for (int i = 0; gargv[i]; i++)
//	{
//		printf("agrv[%d}: %s\n", i, gargv[i]);
//	}
//}

//执行解析好的命令，为了防止程序崩溃挂掉，让子进程执行
bool ExeCmd()
{
	pid_t id = fork();
	if (id < 0)
	{
		return false;
	}
	if (id == 0)
	{
		//将myshell的环境变量表传给子进程
		execvpe(gargv[0], gargv, genv); 
	    exit(1);
	}
	
	int status = 0;
	pid_t rid = waitpid(id, &status, 0);
	if (rid > 0)
	{
		if (WIFEXITED(status))
		{
			lastcode = WEXITSTATUS(status);
		}
		else
		{
			lastcode = 100;
		}
		return true;
	}
	return false;
}

void AddEnv(char *item)
{
	int index = 0;
	while (genv[index])
	{
		index++;
	}
	genv[index] = (char*)malloc(strlen(item) + 1);
	strncpy(genv[index], item, strlen(item) + 1);
	genv[++index] = NULL;
}

//在shell中，有些命令，必须由子进程执行
//有些命令，只能shell自己执行——内建命令
//shell自己执行命令，本质是shell调用自己的函数
bool CheckAndExeBuiltCmd()
{
	if (strcmp(gargv[0], "cd") == 0)
	{
		//内建命令
		if (gargc == 2)
		{
			chdir(gargv[1]);
		}
		else
		{
			lastcode = 1;
		}
		return true;
	}
	else if (strcmp(gargv[0], "export") == 0)
	{
		if (gargc == 2)
		{
			AddEnv(gargv[1]);
		}
		else
		{
			lastcode = 2;
		}
		return true;
	}
	else if (strcmp(gargv[0], "env") == 0)
	{
		for (int i = 0; genv[i]; i++)
		{
			printf("%s\n", genv[i]);
		}
		lastcode = 0;
		return true;
	}
	else if (strcmp(gargv[0], "echo") == 0)
	{
		if (gargc == 2)
		{
			if (gargv[1][0] == '$')
			{
				if (gargv[1][1] == '?')
				{
					printf("%d\n", lastcode);
					lastcode = 0;
				}
			}
			else
			{
				printf("%s\n", gargv[1]);
				lastcode = 0;
			}
		}
		else
		{
			lastcode = 3;
		}
		return true;
	}
	return false;
}

//作为一个shell，获取环境变量应该从系I统的配置文件中读取
//这里直接从父shell中拷贝
void InitEnv()
{
    extern char **environ;
	int index = 0;
	while (environ[index])
	{
		genv[index] = (char*)malloc(strlen(environ[index]) + 1);
		strncpy(genv[index], environ[index], strlen(environ[index] + 1));
		index++;
	}
	genv[index] = NULL;
}

int main()
{
	InitEnv();
	char cmd_buffer[basesize];//获取指令缓冲区
	while (true)
	{	
        PrintCmdLine();//1、命令行提示符
		if(!GetCmdLine(cmd_buffer, basesize))//2、获取用户命令
		{
			continue;
		}
		//printf("%s\n", cmd_buffer);
	    ParseCmdLine(cmd_buffer, strlen(cmd_buffer));//3、分析命令
		//debug(); 
		if (CheckAndExeBuiltCmd())
		{
			continue;
		}
		ExeCmd();//4、执行命令
	}
	return 0;
}
